/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.business.fee.core.repository.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import info.peigen.hotpot.business.account.core.domain.account.Account;
import info.peigen.hotpot.business.account.core.repository.service.AccountRepository;
import info.peigen.hotpot.business.fee.core.domain.Fee;
import info.peigen.hotpot.business.fee.core.domain.FeeLog;
import info.peigen.hotpot.business.fee.core.domain.FeeRule;
import info.peigen.hotpot.business.fee.core.repository.entity.FeeLogEntity;
import info.peigen.hotpot.business.fee.core.repository.mapper.FeeLogMapper;
import info.peigen.hotpot.business.fee.facade.enums.FeeAction;
import info.peigen.hotpot.business.fee.facade.enums.FeeProductCode;
import info.peigen.hotpot.business.fee.facade.enums.FeeType;
import info.peigen.hotpot.business.router.core.service.receipt.QueryReceiptChannelService;
import info.peigen.hotpot.common.core.enums.Currency;
import info.peigen.hotpot.common.core.result.ResultCode;
import info.peigen.hotpot.common.service.base.exception.BusinessException;
import info.peigen.hotpot.component.data.repository.beetlsql.service.AbstractBeetlSqlRepository;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.FieldDefaults;
import org.beetl.sql.core.query.Query;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <b>(FeeRepository)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2021/8/5
 */
@Data
@EqualsAndHashCode(callSuper = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
@Component
public class FeeRepository extends AbstractBeetlSqlRepository<FeeLogMapper, FeeLogEntity> {
    @Inject
    FeeLogRepository           feeLogRepository;
    @Inject
    FeeRuleRepository          feeRuleRepository;
    @Inject
    AccountRepository          accountRepository;
    @Inject
    QueryReceiptChannelService queryReceiptChannelService;

    public void storeFeeLog(FeeLog domain) {
        feeLogRepository.store(domain);
    }

    public Fee active(String tradeId) {
        List<FeeLogEntity> feeLogEntities = getMapper().findByTradeId(tradeId);
        if (CollUtil.isNotEmpty(feeLogEntities)) {
            FeeLogEntity feeLogEntity = feeLogEntities.stream().filter(entity -> FeeAction.Calculate == entity.getAction())
                    .findFirst().orElse(null);
            if (ObjectUtil.isNotNull(feeLogEntity)) {
                Account chargeAccount = accountRepository.active(feeLogEntity.getAccountId());
                Assert.notNull(chargeAccount, () -> new BusinessException(ResultCode.FAILURE, "[收费]激活账户时失败![accountId=" + feeLogEntity.getAccountId() + "]"));
                Account feeAccount = accountRepository.active(feeLogEntity.getPayeeAccountId());
                Assert.notNull(feeAccount, () -> new BusinessException(ResultCode.FAILURE, "[收费]激活账户时失败![accountId=" + feeLogEntity.getAccountId() + "]"));

                return Fee.builder().feeAmount(feeLogEntity.getFeeAmount()).action(feeLogEntity.getAction())
                        .chargeAccount(chargeAccount)
                        .code(feeLogEntity.getCode())
                        .feeAccount(feeAccount).build();
            }
        }
        return null;
    }

    public List<FeeLog> activeFeelogs(String tradeId) {
        List<FeeLogEntity> feeLogEntities = getMapper().findByTradeId(tradeId);
        return feeLogEntities.stream().map(this::active).collect(Collectors.toList());
    }

    public FeeLog active(FeeLogEntity feeLogEntity) {
        FeeLog feeLog = BeanUtil.copyProperties(feeLogEntity, FeeLog.class);

        Account chargeAccount = accountRepository.active(feeLogEntity.getAccountId());
        feeLog.setAccount(chargeAccount);
        Assert.notNull(chargeAccount, () -> new BusinessException(ResultCode.FAILURE, "[收费]激活账户时失败![accountId=" + feeLogEntity.getAccountId() + "]"));

        Account feeAccount = accountRepository.active(feeLogEntity.getPayeeAccountId());
        feeLog.setPayeeAccount(feeAccount);
        Assert.notNull(feeAccount, () -> new BusinessException(ResultCode.FAILURE, "[收费]激活账户时失败![accountId=" + feeLogEntity.getAccountId() + "]"));

        return feeLog;
    }

    public void storeFeeRule(FeeRule domain) {
        feeRuleRepository.store(domain);
    }

    public FeeRule activeFeeRule(String ruleId) {
        return feeRuleRepository.active(ruleId);
    }

    public FeeRule activeFeeRule(String accountId, String feeAccountId, FeeType type, FeeProductCode code, Currency currency) {
        Account account = accountRepository.active(accountId);
        FeeRule rule    = feeRuleRepository.active(account, code);
        if (Objects.isNull(account)) {
            account = Account.builder().accountId(accountId).build();
        }
        if (Objects.nonNull(rule)) {
            Account billingAccount = accountRepository.active(rule.getBillingAccount().getAccountId());
            Account chargeAccount  = accountRepository.active(rule.getPayerAccount().getAccountId());
            Account feeAccount     = accountRepository.active(rule.getPayeeAccount().getAccountId());
            return rule.setAccount(account).setPayeeAccount(account).setBillingAccount(billingAccount).setPayerAccount(chargeAccount).setPayeeAccount(feeAccount);
        } else {
            Account feeAccount = accountRepository.active(feeAccountId);
            return FeeRule.builder()
                    .code(code)
                    .type(type)
                    .currency(currency)
                    .account(account)
                    .payeeAccount(feeAccount)
                    .billingAccount(feeAccount)
                    .payerAccount(feeAccount)
                    .build();
        }
    }

    public List<FeeLogEntity> listByTradeIdAndAction(List<String> tradeIds, FeeAction calculate) {
        return getMapper().createLambdaQuery()
                .andIn(FeeLogEntity::getTradeId, Query.filterNull(tradeIds))
                .andEq(FeeLogEntity::getAction, calculate)
                .select();
    }
}